#include "imagen/imagen.h"
#include "matriz/matriz.h"
#include <map>

#define ct 2 // cant de componentes usadas sobre las cuales se proyectan todas

using namespace std;
typedef multimap<string,Imagen> Album;
typedef multimap<string,Imagen>::iterator Iterador;

void showHelp();
Matriz covarianza(Album,int);
void transformarAlbum(Album, const Matriz&, int);
void copiarArreglo(double* fuente, double* destino, int size);
string mejorDistancia(Album, double *, int, string *, int);
Matriz covarianzaTC(Iterador, Iterador , int,double*);

int main(int argv, char** argc){

/// Handling de parámetros

    if(argv < 5){ //parametros insuficientes 
        cout << "*********************************" << endl;
        cout << "Parametros insuficientes!!!!!!! "<< endl;
        cout << "*********************************" << endl << endl << endl;
        showHelp();
        return -1;
    }	

    if (!(atoi(argc[4]) == 1 || atoi(argc[4]) == 0)){
        cout << "**************************************" << endl;
        cout << "Modo de remuestreo incorrecto!!!!!!! "<< endl;
        cout << "**************************************" << endl << endl << endl;
        showHelp();
        return -1;
    }

    double delta = atof(argc[1]);
    cout << "parametro delta: " << delta << endl;
    const char* src = argc[2];
    cout << "el nombre de la imagen es " << src  <<endl;
    nat anchoDest = atoi(argc[3]);
    cout << "el ancho de la imagen remuestrada es " << anchoDest <<endl;

    Imagen test(src, anchoDest );

	// Elección de modo de submuestreo para la imagen de entrada.
	if(atoi(argc[4]) == 0) {
		test.muestreo_naif();
	} else if(atoi(argc[4]) == 1) {
		test.muestreo_promedio();
    } else {
        cout << "TERRIBLE ERROR" << endl;
    }
  
// END_Elección modo submuestreo. //


/* Esta sección puede editarse de tal manera de que uno pueda armar el album que quiera.
Imagen a( ruta de la imagen, anchoDestino, ct = cantidad de componentes proyectadas );
a.muestreo_ // puede ser promedio() o naif()

*/
    
	//CREO EL ALBUM
    
    Imagen a1("faces/aniston1.jpg", anchoDest, ct);
    a1.muestreo_promedio();
    Imagen a2("faces/aniston2.jpg", anchoDest, ct);
    a2.muestreo_promedio();
    Imagen a3("faces/aniston3.jpg", anchoDest, ct);
    a3.muestreo_promedio();
    Imagen a4("faces/aniston4.jpg", anchoDest, ct);
    a4.muestreo_promedio();

    Imagen b1("faces/bush1.jpg", anchoDest, ct);
    b1.muestreo_promedio();
    Imagen b2("faces/bush2.jpg", anchoDest, ct);
    b2.muestreo_promedio();
    Imagen b3("faces/bush3.jpg", anchoDest, ct);
    b3.muestreo_promedio();
    Imagen b4("faces/bush4.jpg", anchoDest, ct);
    b4.muestreo_promedio();

    Imagen c1("faces/jolie1.jpg", anchoDest, ct);
    c1.muestreo_promedio();
    Imagen c2("faces/jolie2.jpg", anchoDest, ct);
    c2.muestreo_promedio();
    Imagen c3("faces/jolie3.jpg", anchoDest, ct);
    c3.muestreo_promedio();
    Imagen c4("faces/jolie4.jpg", anchoDest, ct);
    c4.muestreo_promedio();

    Imagen d1("faces/pitt1.jpg", anchoDest, ct);
    d1.muestreo_promedio();
    Imagen d2("faces/pitt2.jpg", anchoDest, ct);
    d2.muestreo_promedio();
    Imagen d3("faces/pitt3.jpg", anchoDest, ct);
    d3.muestreo_promedio();
    Imagen d4("faces/pitt4.jpg", anchoDest, ct);
    d4.muestreo_promedio();

    Imagen e1("faces/lee1.jpg", anchoDest, ct);
    e1.muestreo_promedio();
    Imagen e2("faces/lee2.jpg", anchoDest, ct);
    e2.muestreo_promedio();
    Imagen e3("faces/lee3.jpg", anchoDest, ct);
    e3.muestreo_promedio();
    Imagen e4("faces/lee4.jpg", anchoDest, ct);
    e4.muestreo_promedio();

    cout << "CREANDO ALBUM" << endl;
   
/*
	Agrego las imagenes al álbum
	album.insert( pair(...) (Grupo, imagen) ;
	y listo el pollo
*/

    Album album;
    album.insert( pair<string,Imagen>("ANISTON",a1));
    album.insert( pair<string,Imagen>("ANISTON",a2));
    album.insert( pair<string,Imagen>("ANISTON",a3));
    album.insert( pair<string,Imagen>("ANISTON",a4));
    album.insert( pair<string,Imagen>("BUSH",b1));
    album.insert( pair<string,Imagen>("BUSH",b2));
    album.insert( pair<string,Imagen>("BUSH",b3));
    album.insert( pair<string,Imagen>("BUSH",b4));
    album.insert( pair<string,Imagen>("JOLIE",c1));
    album.insert( pair<string,Imagen>("JOLIE",c2));
    album.insert( pair<string,Imagen>("JOLIE",c3));
    album.insert( pair<string,Imagen>("JOLIE",c4));
    album.insert( pair<string,Imagen>("PITT",d1));
    album.insert( pair<string,Imagen>("PITT",d2));
    album.insert( pair<string,Imagen>("PITT",d3));
    album.insert( pair<string,Imagen>("PITT",d4));
    album.insert( pair<string,Imagen>("LEE",e1));
    album.insert( pair<string,Imagen>("LEE",e2));
    album.insert( pair<string,Imagen>("LEE",e3));
    album.insert( pair<string,Imagen>("LEE",e4));

// FIN SECCIÓN ALBUM 


    cout << "CREANDO COVARIANZA" << endl;
    Matriz z =  covarianza(album, anchoDest);
    cout << "AUTOCALCULAR" << endl;
    double autoval[anchoDest*anchoDest];
    Matriz autovectores(anchoDest*anchoDest);
    
     if(atoi(argc[5]) == 0) {
        autovectores = z.autocalcular(autoval,delta);
        autovectores.store("grabar");
    } else {
        autovectores.load("grabar");
    }
    //HASTA ACA TENEMOS LOS AUTOVECTORES!
    cout << "TRANSFORMANDO ALBUM" << endl;
    transformarAlbum(album, autovectores, ct);
    //y la imagen a analizar
    double transformacionTest[ct];
    autovectores.transformacionCaracteristica(test.imageData(), transformacionTest, ct);
    cout << "TEST: [" << transformacionTest[0] << "," << transformacionTest[1] << "]" << endl;
    //ahora vemos el de mejor distancia por gurpo
    cout << "CALCULA DISTANCIAS" << endl;
    string grupos[5];
    grupos[0] = "ANISTON";
    grupos[1] = "BUSH";
    grupos[2] = "JOLIE";
    grupos[3] = "PITT";
    grupos[4] = "LEE";
    string quien = mejorDistancia(album, transformacionTest, ct, grupos, 5);
    cout << "LA FOTO ES MUY PARECIDA A: " << quien << endl;
    return 0;
}
///*
void showHelp(){
	cout << "\tAutocaras! (v. 1.0)" << endl;
	cout << "\t====================" << endl << endl << endl;
	cout << "\tModo de uso: " << endl <<;
	cout << "\t\t./autocaras delta imagenFuente ancho metodo cargoMatriz? " << endl << endl;
	cout << "\t\tdelta: tolerancia para el método QR" << endl;
	cout << "\t\timagenDestino: imagen que voy a procesar contra la base de datos" << endl;
	cout << "\t\tancho: ancho en pixeles de la imagen una vez remuestreada" << endl;
	cout << "\t\tmétodo: método de remuestreo: 0 para el naif, 1 para el muestreo por promedios" << endl;
	cout << "\t\tcargoMatriz?: 0 calcula de cero :D, 1 carga la matriz de autovectores desde un archivo de txt, no la calcula" << endl;
	
}
//*/

Matriz covarianza(Album a, int ancho) {
    Iterador it;
    Matriz aux(ancho*ancho);
    double promedio[ancho*ancho];
    for(unsigned int i=0;i<ancho*ancho;i++) {
        promedio[i] = 0;
    }
    int cantidad=0;
    for ( it=a.begin() ; it != a.end(); it++ ) {
        for(unsigned int i=0;i<ancho;i++) {
            for(unsigned int j=0;j<ancho;j++) {
                promedio[i*ancho + j] += (double)(*it).second.pos(i,j);
                aux.setPos(cantidad, i*ancho+j, (*it).second.pos(i,j));
            }
        }
        cantidad++;
    }
    //cout << aux << endl;
    //hasta aca en promedio[] tenemos el prmediode las cosas
    for(unsigned int i=0;i<ancho*ancho;i++) {
        promedio[i] = (double)promedio[i]/cantidad;
    }
    for(unsigned int i=0;i<cantidad;i++) {
        for(unsigned int j=0;j<ancho*ancho;j++) {
            aux.setPos(i,j,aux(i,j) -  promedio[j]);
        }
    }
    //datos en la matriz a usar para hacer los calculos
    Matriz cov(ancho*ancho);
    for(unsigned int c=0;c<cantidad;c++) {
        for(unsigned int i=0;i<ancho*ancho;i++) {
            for(unsigned int j=i;j<ancho*ancho;j++) {
                cov.setPos(i,j, cov(i,j) + aux(c, i)*aux(c,j));
            }
        }
    }
    for(unsigned int i=0;i<ancho*ancho;i++) {
        for(unsigned int j=i+1;j<ancho*ancho;j++) {
            cov.setPos(j,i, cov(i,j));
        }
    }
    cov.producto((double)1/(cantidad-1));
    //cout << cov << endl;
    return cov;
}

void transformarAlbum(Album a, const Matriz& m, int cantidad) {
    Iterador it;
    for ( it=a.begin() ; it != a.end(); it++ ) {
        m.transformacionCaracteristica( (*it).second.imageData() , (*it).second.transformada, cantidad );
        cout << (*it).second.name << ": [" << (*it).second.transformada[0] << ","<< (*it).second.transformada[1] << "]" << endl;
    }
}

string mejorDistancia(Album a, double * test, int cantCoordenadas, string * grupos, int cantGrupos) {
    //PARA CADA GRUPO CALCULAMOS SU DISTANCIA MAHALANOBI Y NOS QUEDAMOS CON LA DE MENRO DISTANCIA
    double distancia[cantCoordenadas];
    for(int i=0;i<cantGrupos;i++) {
        pair<Iterador, Iterador> group = a.equal_range(grupos[i]);
        double promedioGrupo[cantCoordenadas];
        Matriz cov = covarianzaTC(group.first, group.second, cantCoordenadas, promedioGrupo);
        cout << cov << endl;
        cov.inversa();
        for(int j=0;j<cantCoordenadas;j++) {
            test[j] = test[j] - promedioGrupo[j];
        }
        double values[cantCoordenadas];
        //cout << test[0] << "," << test[1] << " <<----" << endl;
        for(int j=0;j<cantCoordenadas;j++) {
            double val = 0;
            for(int k=0;k<cantCoordenadas;k++) {
                val += test[k]*cov(k,j);
            }
            values[j] = val;
        }
        //cout << values[0] << " , " << values[1] << "  lala <<" << endl;
        double valFinal = 0;
        for(int j=0;j<cantCoordenadas;j++) {
            valFinal += values[j]*test[j];
        }
        distancia[i] = sqrt(valFinal);
        cout << "FINAL(" << grupos[i] << "): " << distancia[i] << endl;
    }
    int indice = 0;
    for(int i=0;i<cantGrupos;i++) {
        if(distancia[i] < distancia[indice]) {
            indice = i;
        }
    }
    return grupos[indice];
}

Matriz covarianzaTC(Iterador inicial, Iterador final, int coordenadas, double * promedio) {
    Iterador it;
    for(unsigned int i=0;i<coordenadas;i++) {
        promedio[i] = 0;
    }
    int cantAux=0;
    for ( it=inicial; it != final; it++ ) {
        cantAux++;
    }
    Matriz aux(cantAux);
    int cantidad=0;
    for ( it=inicial; it != final; it++ ) {
        for(unsigned int i=0;i<coordenadas;i++) {
            promedio[i] += (*it).second.transformada[i];
            aux.setPos(cantidad, i, (*it).second.transformada[i]);
        }
        cantidad++;
    }
    //cout << "AUX: " << endl;
    //cout << aux << endl;
    //hasta aca en promedio[] tenemos el prmediode las cosas
    for(unsigned int i=0;i<coordenadas;i++) {
        promedio[i] = (double)promedio[i]/cantidad;
    }
    for(unsigned int i=0;i<cantidad;i++) {
        for(unsigned int j=0;j<coordenadas;j++) {
            aux.setPos(i,j,aux(i,j) -  promedio[j]);
        }
    }
    //datos en la matriz a usar para hacer los calculos
    Matriz cov(coordenadas);
    for(unsigned int c=0;c<cantidad;c++) {
        for(unsigned int i=0;i<coordenadas;i++) {
            for(unsigned int j=i;j<coordenadas;j++) {
                cov.setPos(i,j, cov(i,j) + aux(c, i)*aux(c,j));
            }
        }
    }
    for(unsigned int i=0;i<coordenadas;i++) {
        for(unsigned int j=i+1;j<coordenadas;j++) {
            cov.setPos(j,i, cov(i,j));
        }
    }
    cov.producto((double)1/(cantidad-1));
    //cout << "COV: " << endl;
    //cout << cov << endl;
    return cov;
}


